package chat;

import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import MainGUI.MainServerGUI;

/**
 * Server Thread class for use in ACE4.
 * @author derykegan
 *
 */

public class ChatThread extends Thread{
	
	Socket client;
	int threadID;
	String serverName;
	SyncChat messages;
	Calendar theTime;
	private boolean debugMode = false;
	
	boolean standalone;
	
	MainServerGUI gui;
	
	SimpleDateFormat format = new SimpleDateFormat("HH:mm:ss");
	
	/**
	 * Creates new thread for chat server.
	 * @param socket	-	Socket object for client session
	 * @param threadID	-	ID of this thread
	 * @param serverName-	Name of the server
	 * @param messages	-	Reference to messages store
	 * @param debug		-	True/false for verbose errors/status.
	 */
	public ChatThread(Socket socket, int threadID, 
			String serverName, SyncChat messages, boolean debug){
		this.client = socket;
		this.threadID = threadID;
		this.serverName = serverName;
		this.messages = messages;
		this.theTime = Calendar.getInstance();
		this.debugMode = debug;
		
		standalone = true;
	}
	
	
	/**
	 * Creates new thread for chat server but with ref to GUI.
	 * @param socket	-	Socket object for client session
	 * @param threadID	-	ID of this thread
	 * @param serverName-	Name of the server
	 * @param messages	-	Reference to messages store
	 * @param debug		-	True/false for verbose errors/status.
	 */
	public ChatThread(Socket socket, int threadID, 
			String serverName, SyncChat messages, MainServerGUI gui, boolean debug){
		this.client = socket;
		this.threadID = threadID;
		this.serverName = serverName;
		this.messages = messages;
		this.theTime = Calendar.getInstance();
		this.debugMode = debug;
		this.gui = gui;
		
		standalone = false;
	}
	
	
	/**
	 * Container class for each server thread. Keeps track
	 * if we've read in a message and what it is.
	 * @author derykegan
	 *
	 */
	class ReadIn{
		
		private boolean changed = false;
		private ChatProtocol newMessage;
		
		public ReadIn(){
			
		}
		
		synchronized public void addMessage(ChatProtocol message){
			this.newMessage = message;
			changed = true;
		}
		
		synchronized public boolean getChanged(){
			return changed;
		}
		
		synchronized public ChatProtocol getMessage(){
			changed = false;
			return newMessage;
		}
		
	}
	
	
	/**
	 * Main execution thread code
	 */
	public void run(){
		try{
			if(debugMode)
				writeUpdate("New thread created, " +
						"session ID: " + threadID);
			
			ObjectOutputStream oos = new ObjectOutputStream
					(client.getOutputStream());
			InputStream in = null;
			
			/* updates calendar to current time */
			theTime = Calendar.getInstance();
			
			/* write welcome message to client from server */
			oos.writeObject(new ChatProtocol(
					ChatProtocol.SERVER_STATUS_MESSAGE,
					new Message(
							format.format(theTime.getTime()),
							null,
							"Welcome to " + serverName))
			);
			oos.reset();
			oos.flush();
			
			if(debugMode)
				writeUpdate("Welcome message sent.");
			
			in = client.getInputStream();
	//		ObjectInputStream ois = new ObjectInputStream(in);
			
			boolean executing = true;
			int messageCount = messages.getCount();
			
			ReadIn readin = new ReadIn();
			ChatThreadListen listenThread = new ChatThreadListen(in, readin);
			listenThread.start();
			
			/* ----- MAIN SERVER EXECUTION LOOP  ------ */
			while(executing){
				
				/* if there have been messages since we last checked... */
				if(messages.getCount() > messageCount){
					if(debugMode)
						writeUpdate("Found new messages");
					/*... get the messages that have been submitted */
					ArrayList<Message> since = 
							messages.getMessagesSince(messageCount);
					messageCount = messages.getCount();
					
					if(since.size() <= 0){
						writeUpdate("Error: shouldnt be here - " +
								"messages not received");
						break;
					}
					
					/* now send each to client */
					for(Message m : since){
						oos.writeObject(new ChatProtocol(
								ChatProtocol.MESSAGE_FROM_SERVER,
								m));
						oos.reset();
						oos.flush();
					}
					
					/* and print to console */
					for(Message m : since){
						writeUpdate(m.getUsername() + ": " 
								+ m.getMessageText());
					}
					
				}
				
				/* if we've received message(s) from client... */
				ChatProtocol fromClient;
				if(readin.getChanged()){
					fromClient = readin.getMessage();
					
					if(fromClient != null){
						if(debugMode)
							writeUpdate("Message received from client.");
						
						/* check what kind of message it is */
						if(fromClient.getState() == ChatProtocol.CLIENT_DISCONNECT){
							/* if the client has sent a disconnect request, close */
							executing = false;
							listenThread.shutDown();
							break;
						}
						
						/* if the client has sent us a message, add to message pool */
						if(fromClient.getState() == ChatProtocol.MESSAGE_TO_SERVER){
							messages.addMessage(fromClient.getMessage());
						}
				}
				
				
				
				}
				if(debugMode)
					writeUpdate("looping\n\n\n\n");
				
				/* give other threads a chance to execute */
				Thread.sleep(50);
			}
			
			
		}
		catch(Exception e){
			System.out.println("Error: " + e);
		}
	}
	
	/**
	 * Private update method to write strings to either console or
	 * GUI.
	 * @param update
	 */
	
	private void writeUpdate(String update){
		if(standalone)
			System.out.println(update);
		else
			gui.updateChatString(update + "\n");
	}

}
